#pragma once
#include <condition_variable>
#include <type_traits>
#include <filesystem>
#include <syncstream>
#include <iostream>
#include <concepts>
#include <fstream>
#include <vector>
#include <thread>
#include <format>
#include <atomic>
#include <chrono>
#include <mutex>
#include <array>
#include <map>

#include <boost/lockfree/spsc_queue.hpp>

#include "compatible.hpp"
#include "setting.hpp"
#include "utility.hpp"
#include "level.hpp"

namespace my_log
{
	class flusher;
	class screen_output;
	class file_output;
	class roll_file_output;

	// 静态多态注册表
	using output_t = std::variant<screen_output, file_output, roll_file_output>;

	// 输出方式基类
	// 输出方式类使用CRTP静态多态设计
	// 因此禁止使用delete销毁基类指针，请使用destroy()
	template<typename Class_type>
	class output_base
	{
	protected:
		output_base() = default;

		output_base(const output_base& other) = delete;

		output_base(output_base&& other) = default;
	public:
		// 输出接口函数
		// 派生类必须实现该函数
		void landing(std::string_view input, unsigned int level, flusher& f, output_t* call_back)
		{
			static_cast<Class_type*>(this)->landing(input, level, f, call_back);
		}

		// 删除函数
		// 使用delete销毁基类指针将造成内存泄漏
		// 派生类必须实现该函数
		void destroy()
		{
			static_cast<Class_type*>(this)->destory();
		}

		// 刷新函数
		// 使用delete销毁基类指针将造成内存泄漏
		// 派生类必须实现该函数
		void flush()
		{
			static_cast<Class_type*>(this)->flush();
		}
	};

	// 显示器输出
	class screen_output : public output_base<screen_output>
	{
		friend class output_base;
	public:
		screen_output() = default;
		screen_output(const screen_output& other) = delete;
		screen_output(screen_output&& other) noexcept = default;
	private:
		// 派生类输出函数
		void landing(std::string_view input, unsigned int level, flusher& f, output_t* call_back);

		// 派生类删除函数
		void destroy();

		// 派生类刷新函数
		void flush();
	};

	// 文件输出
	class file_output : public output_base<file_output>
	{
		friend class output_base;
	public:
		file_output(const std::filesystem::path& name, size_t buf_size = DEFAULT_STREAM_BUFFER_SIZE);
		~file_output();
		file_output(const file_output& other) = delete;
		file_output(file_output&& other) noexcept;
	private:
		// 派生类输出函数
		void landing(std::string_view input, unsigned int level, flusher& f, output_t* call_back);

		// 派生类删除函数
		void destory();

		// 派生类刷新函数
		void flush();
	private:
		// 文件流输出
		std::ofstream _fout;
		// 自定义缓冲区
		std::vector<char> _buf;
	};

	// 滚动文件输出
	class roll_file_output : public output_base<roll_file_output>
	{
		friend class output_base;
	public:
		roll_file_output(const std::filesystem::path& name_base, size_t single_file_size = 0, size_t buf_size = DEFAULT_STREAM_BUFFER_SIZE);
		~roll_file_output();
		roll_file_output(const roll_file_output& other) = delete;
		roll_file_output(roll_file_output&& other) noexcept;

		// 获取最大单个文件大小
		const std::atomic<size_t>& getMaxSingleFileSize() noexcept;

		// 设置最大单个文件大小
		void setMaxSingleFileSize(size_t num) noexcept;

		// 子文件名生成器
		std::filesystem::path nameGenerator() const noexcept;

		// 默认单个文件大小
		constexpr static size_t defaultSingleFileSize() noexcept;

		// 最小单个文件大小
		constexpr static size_t MinSingleFileSize() noexcept;
	private:
		// 派生类输出函数
		void landing(std::string_view input, unsigned int level, flusher& f, output_t* call_back);

		// 派生类删除函数
		void destory();

		// 派生类刷新函数
		void flush();
	private:
		// 滚动文件基本名
		std::filesystem::path _name_base;
		// 文件流输出
		std::ofstream _fout;
		// 当前文件已写入字节数
		std::atomic<size_t> _written_byte;
		// 最大单位文件大小
		std::atomic<size_t> _max_single_file_size;
		// 文件切换锁
		std::mutex _ctrl;
		// 自定义缓冲区
		std::vector<char> _buf;
	};

	// 输出方式访问者
	struct landing_visitor
	{
		landing_visitor(flusher& f)
			: _str_storge()
			, _str_view()
			, _level(0)
			, _f(f)
			, _self_ptr(nullptr)
		{}

		landing_visitor(std::string_view str_view, flusher& f)
			: _str_storge()
			, _str_view(str_view)
			, _level(0)
			, _f(f)
			, _self_ptr(nullptr)
		{}

		landing_visitor(const std::string& str, flusher& f)
			: _str_storge(str)
			, _str_view(_str_storge)
			, _level(0)
			, _f(f)
			, _self_ptr(nullptr)
		{}

		landing_visitor(std::string&& str, flusher& f)
			: _str_storge(std::move(str))
			, _str_view(_str_storge)
			, _level(0)
			, _f(f)
			, _self_ptr(nullptr)
		{}

		void setString(std::string_view str_view) noexcept
		{
			_str_view = str_view;
		}

		void setString(const std::string& str, bool need_storge = true) noexcept
		{
			if (need_storge)
			{
				_str_storge = str;
				_str_view = _str_storge;
			}
			else
			{
				_str_view = str;
			}
		}

		void setString(std::string&& str) noexcept
		{
			_str_storge = std::move(str);
			_str_view = _str_storge;
		}

		void setLevel(log_level::value level) noexcept
		{
			_level = static_cast<int>(level);
		}

		void setSelf(output_t* self_ptr) noexcept
		{
			_self_ptr = self_ptr;
		}

		// 访问者接口
		template<typename T>
		void operator()(T& output)
		{
			output.output_base<T>::landing(_str_view, _level, _f, _self_ptr);
		}

		// 字符串存储(可选)
		std::string _str_storge;
		// 已格式化字符串视图
		std::string_view _str_view;
		// 当前日志等级
		int _level;
		// 刷新器引用
		flusher& _f;
		// 联合体指针
		output_t* _self_ptr;
	};

	// 刷新访问者
	struct flush_visitor
	{
		// 访问者接口
		template<typename T>
		void operator()(T& output)
		{
			output.output_base<T>::flush();
		}
	};

	namespace flush_strategy
	{
		using dura = std::chrono::milliseconds;

		// 刷新策略基类
		class strategy_base
		{
		protected:
			constexpr strategy_base(const std::array<dura, 5>& level_times, const dura& duration, const size_t& count)
				: _level_times(level_times)
				, _printer_startup_duration(duration)
				, _printer_startup_count(count)
			{}
		public:
			// 各日志等级对应的最长保留时间
			const std::array<dura, 5>& _level_times;
			// 打印线程最长启动间隔
			const dura _printer_startup_duration;
			// 打印线程最多可积压日志条数
			const size_t _printer_startup_count;
		};

		// 实时系统，零延迟
		class realtime_t : public strategy_base
		{
		public:
			constexpr realtime_t()
				: strategy_base(_level_times, _printer_startup_duration, _printer_startup_count)
			{}
		private:
			// 各日志等级对应的最长保留时间
			constexpr static std::array<dura, 5> _level_times = { dura{ 0 }, dura{ 0 }, dura{ 0 }, dura{ 0 }, dura{ 0 } };
			// 打印线程最长启动间隔(该值无意义)
			constexpr static dura _printer_startup_duration = dura{ 100000000 };
			// 打印线程最多可积压日志条数
			constexpr static size_t _printer_startup_count = 1;
		};

		// 100ms，高频业务
		class fast_t : public strategy_base
		{
		public:
			constexpr fast_t()
				: strategy_base(_level_times, _printer_startup_duration, _printer_startup_count)
			{}
		private:
			// 各日志等级对应的最长保留时间
			constexpr static std::array<dura, 5> _level_times = { dura{ 100 }, dura{ 100 }, dura{ 20 }, dura{ 0 }, dura{ 0 } };
			// 打印线程最长启动间隔
			constexpr static dura _printer_startup_duration = dura{ 20 };
			// 打印线程最多可积压日志条数
			constexpr static size_t _printer_startup_count = 5;
		};

		// 1s，通用业务
		class balanced_t : public strategy_base
		{
		public:
			constexpr balanced_t()
				: strategy_base(_level_times, _printer_startup_duration, _printer_startup_count)
			{}
		private:
			// 各日志等级对应的最长保留时间
			constexpr static std::array<dura, 5> _level_times = { dura{ 1000 }, dura{ 1000 }, dura{ 200 }, dura{ 50 }, dura{ 0 } };
			// 打印线程最长启动间隔
			constexpr static dura _printer_startup_duration = dura{ 50 };
			// 打印线程最多可积压日志条数
			constexpr static size_t _printer_startup_count = 12;
		};

		// 5s，后台任务
		class lazy_t : public strategy_base
		{
		public:
			constexpr lazy_t()
				: strategy_base(_level_times, _printer_startup_duration, _printer_startup_count)
			{}
		private:
			// 各日志等级对应的最长保留时间
			constexpr static std::array<dura, 5> _level_times = { dura{ 5000 }, dura{ 5000 }, dura{ 1000 }, dura{ 200 }, dura{ 0 } };
			// 打印线程最长启动间隔
			constexpr static dura _printer_startup_duration = dura{ 200 };
			// 打印线程最多可积压日志条数
			constexpr static size_t _printer_startup_count = 50;
		};

		// 按批次，最高性能
		class batch_t : public strategy_base
		{
		public:
			constexpr batch_t()
				: strategy_base(_level_times, _printer_startup_duration, _printer_startup_count)
			{}
		private:
			// 各日志等级对应的最长保留时间
			constexpr static std::array<dura, 5> _level_times = { dura{ 10000 }, dura{ 10000 }, dura{ 10000 }, dura{ 10000 }, dura{ 10000 } };
			// 打印线程最长启动间隔
			constexpr static dura _printer_startup_duration = dura{ 2000 };
			// 打印线程最多可积压日志条数
			constexpr static size_t _printer_startup_count = 1000;
		};

		constexpr realtime_t realtime;
		constexpr fast_t fast;
		constexpr balanced_t balanced;
		constexpr lazy_t lazy;
		constexpr batch_t batch;
	}

	// 缓冲区刷新器
	class flusher
	{
	private:
		using dura = std::chrono::milliseconds;
		using t_point = std::chrono::steady_clock::time_point;
	private:
		// 计时器
		struct timer
		{
		public:
			timer(output_t* target_ptr);

			// 有无正在进行的计时
			bool empty() noexcept;

			// 返回剩余时间
			// -1: 无计时
			// 0: 未到期
			// 1: 已到期
			int timeLeft(dura& duration) const;

			// 执行刷新操作
			void timeout();

			// 重设计时器
			void reset(unsigned int level, dura duration);
		public:
			// 计时起始点
			t_point _begin;
			// 目标间隔
			dura _duration;
			// 回调指针
			output_t* _call_back_ptr;
			// 当前计时日志等级
			int _level;
		};
	public:
		flusher(const std::atomic<size_t>& status, const std::array<dura, 5>& level_times);
		~flusher();

		// 发送计时器更新请求
		void update(output_t* target_ptr, int level);

		// 添加计时器
		void addTimer(output_t* target_ptr);

		// 是否立即刷新
		bool isImmediate(int level) const;
	private:
		// 刷新线程入口
		void entry();

		// 是否发送更新请求
		bool needUpdate(output_t* target_ptr, int level) const;
	private:
		// 无锁更新请求队列
		boost::lockfree::spsc_queue<std::pair<output_t*, int>, boost::lockfree::capacity<1024>> _timer_update_queue;
		// 计时器表
		std::map<output_t*, timer> _timer_table;
		// 各日志等级对应的最长保留时间
		const std::array<dura, 5>& _level_times;
		// 日志器状态
		// 0: 关闭
		// 1: 启动
		// 2: 等待退出
		const std::atomic<size_t>& _status;
		// 各日志等级最长保留时间是否一致
		bool _is_flush_time_same;
		// 刷新线程
		std::thread _th;
		// 刷新线程条件变量
		std::condition_variable _cond;
		// 刷新线程锁
		std::mutex _ctrl;
	private:
		// 需要刷新线程忙等待的剩余时间
		constexpr static dura _limition = dura{ 1 };
	};
}